/*
 * promise-tracker - v2.0.0 - 2014-04-11
 * http://github.com/ajoslin/angular-promise-tracker
 * Created by Andy Joslin; Licensed under Public Domain
 */

// 监控 promise 的进度情况(是否完成, 是否正在处理中)
//
// Api:
//      active 返回 true/false 是否完成
//      tracking 返回 true/false 是否正在 loading 中
//      destroy/cancel 取消掉监控的 promise
//      createPromise 基本算是私有方法
//      addPromise 添加要监控的 promise

AModule.provider('A.PromiseTracker', function () {
        var trackers = {};

        this.$get = ['$q', '$timeout', function ($q, $timeout) {
            function cancelTimeout(promise) {
                if (promise) {
                    $timeout.cancel(promise);
                }
            }

            return function PromiseTracker(options) {
                //do new if user doesn't
                if (!(this instanceof PromiseTracker)) {
                    return new PromiseTracker(options);
                }

                options = options || {};

                //Array of promises being tracked
                var tracked = [];
                var self = this;

                //Allow an optional "minimum duration" that the tracker has to stay active for.
                var minDuration = options.minDuration;
                //Allow a delay that will stop the tracker from activating until that time is reached
                var activationDelay = options.activationDelay;

                var minDurationPromise;
                var activationDelayPromise;

                self.active = function () {
                    //Even if we have a promise in our tracker, we aren't active until delay is elapsed
                    if (activationDelayPromise) {
                        return false;
                    }
                    return tracked.length > 0;
                };

                self.tracking = function () {
                    //Even if we aren't active, we could still have a promise in our tracker
                    return tracked.length > 0;
                };

                self.destroy = self.cancel = function () {
                    minDurationPromise = cancelTimeout(minDurationPromise);
                    activationDelayPromise = cancelTimeout(activationDelayPromise);
                    for (var i = tracked.length - 1; i >= 0; i--) {
                        tracked[i].resolve();
                    }
                    tracked.length = 0;
                };

                //Create a promise that will make our tracker active until it is resolved.
                // @return deferred - our deferred object that is being tracked
                self.createPromise = function () {
                    var deferred = $q.defer();
                    tracked.push(deferred);

                    //If the tracker was just inactive and this the first in the list of
                    //promises, we reset our delay and minDuration
                    //again.
                    if (tracked.length === 1) {
                        if (activationDelay) {
                            activationDelayPromise = $timeout(function () {
                                activationDelayPromise = cancelTimeout(activationDelayPromise);
                                startMinDuration();
                            }, activationDelay);
                        } else {
                            startMinDuration();
                        }
                    }

                    deferred.promise.then(onDone(false), onDone(true));

                    return deferred;

                    function startMinDuration() {
                        if (minDuration) {
                            minDurationPromise = $timeout(angular.noop, minDuration);
                        }
                    }

                    //Create a callback for when this promise is done. It will remove our
                    //tracked promise from the array if once minDuration is complete
                    function onDone(isError) {
                        return function (value) {
                            (minDurationPromise || $q.when()).then(function () {
                                var index = tracked.indexOf(deferred);
                                tracked.splice(index, 1);

                                //If this is the last promise, cleanup the timeouts
                                //for activationDelay
                                if (tracked.length === 0) {
                                    activationDelayPromise = cancelTimeout(activationDelayPromise);
                                }
                            });
                        };
                    }
                };

                self.addPromise = function (promise) {
                    promise = promise && (promise.$promise || promise) || {};
                    if (!promise.then) {
                        throw new Error("promiseTracker#addPromise expects a promise object!");
                    }
                    var deferred = self.createPromise();

                    //When given promise is done, resolve our created promise
                    //Allow $then for angular-resource objects
                    promise.then(function success(value) {
                        deferred.resolve(value);
                        return value;
                    }, function error(value) {
                        deferred.reject(value);
                        return $q.reject(value);
                    });

                    return deferred;
                };
            };
        }];
    });

